home *** CD-ROM | disk | FTP | other *** search
- /*
- File: FractalMain.c
-
- Used to build: “Fractal 1”
-
- Written by: Jim Cathey July 1985
- Eric Traut November 1994
-
- Description:
- The following code implements a “Fractal Contour” generating
- program. The program was originally written in Microsoft BASIC
- and ported to Aztec C in 1985 by Jim Cathey. It has been a
- widely-distributed public domain application since that time.
-
- The program generates a fractal surface by starting with a
- triangular surface. It then subdivides the triangle into four
- subtriangles by calculating the midpoint of each edge. The
- Z coordinate for each of these edges is then randomly incremented
- or decremented. This processes is repeated for each of the
- subtrianges until a jagged fractal surface results.
-
- The original program uses a “cordic” function to approximate
- the calculation of various transcendental functions used in
- three-dimensional graphics transforms.
-
- In 1994, Eric Traut modified the code to run under System 7.5.
- The program was also updated so it could be recompiled under
- PowerPC. This source is part of a series of modifications made
- to demonstrate code optimizations under PowerPC.
-
- ------------------------------------------------------------
-
- The following documents the changes made to the program at
- each step of the optimization process and lists the speed
- as measured under a PowerMac 8100/80av (8-bit 13"monitor).
- All stages were compiled using Metrowerks’ CodeWarrier
- 1.2 compilers.
-
-
- • STAGE 1:
- 0.59 fractals per second
-
- The first step to optimizing for PowerPC is to clean up
- the source code so it compiles under today’s 68K compilers and
- runs under today’s system software. I also added a feature
- to allow repeated calculation of fractals and a display
- of “fractals per second.” This value will be used to measure
- our progress as we further optimize the program. Note that
- this version runs emulated on a PowerMac.
- */
-
-
- #include <Types.h>
- #include <Memory.h>
- #include <Quickdraw.h>
- #include <Fonts.h>
- #include <Windows.h>
- #include <OSUtils.h>
- #include <Menus.h>
- #include <Events.h>
- #include <TextEdit.h>
- #include <Dialogs.h>
- #include <Desk.h>
- #include <Controls.h>
- #include <ToolUtils.h>
- #include <Resources.h>
- #include <Strings.h>
-
- #include <stdio.h>
-
- #include "Fractal.h"
-
- /* Functions defined within the file */
- void InitApp(void);
- void SetUpMenus(void);
- void SetUpWindow(void);
- void MainEventLoop(void);
- void UpdateWindow(WindowPtr theWindow);
- void DrawTimeInfo(void);
- Boolean DoMenuCommand(long mresult);
- void DoSetUpDialog(void);
- void SetTerrainButton(DialogPtr ptr, short oncontrl, short offcontrl);
- void DoAboutBox(void);
- void ReportFatalError(void);
-
- /* Global variables */
- short (*gPointArray)[kMaxXPoint][kMaxYPoint] = NULL;
- /* The array of points to be subdivided */
- short gContourType; /* Contour type */
- short gContourLevel; /* Level of detail */
- WindowPtr gMainWindow; /* Our one window */
- short gMainWindowHeight; /* Height of main window */
- short gMainWindowWidth; /* Width of main window */
- Boolean gContinuousRedraw; /* Continuously redraw fractal */
- long gTotalTickCount; /* Total time spent calculating and drawing */
- long gTotalFractals; /* Total fractal count */
- Boolean gTimeUpdate; /* Should this screen update be counted? */
-
- /*
- main
- */
- void main(void)
- {
- InitApp();
- MainEventLoop();
- }
-
-
- /*
- MainEventLoop
- */
- void MainEventLoop(void)
- {
- EventRecord curEvent; /* Event we should respond to */
- WindowPtr whichWindow; /* Window which received the click */
- Boolean userDone; /* Should we quit the program? */
-
- userDone = false;
-
- /* Get next event, and handle it appropriately, until user quits */
- while (!userDone) {
- if (WaitNextEvent(everyEvent, &curEvent, 1, NULL)) {
- switch (curEvent.what) {
- case mouseDown:
- switch (FindWindow(curEvent.where, &whichWindow)) {
- case inSysWindow: /* handle the desk accessories */
- SystemClick(&curEvent, whichWindow);
- break;
- case inMenuBar: /* handle the command */
- userDone = DoMenuCommand(MenuSelect(curEvent.where));
- break;
- case inDrag: /* No Drag region, treat as content */
- {
- Rect limitRect;
-
- limitRect = qd.screenBits.bounds;
- InsetRect(&limitRect, 4, 4);
- DragWindow(gMainWindow, curEvent.where, &limitRect);
- break;
- }
- case inContent: /* Activate window */
- if (whichWindow == gMainWindow)
- if (whichWindow != FrontWindow())
- SelectWindow(whichWindow);
- break;
- case inGrow: /* No Grow Region */
- case inGoAway: /* We don’t have a GoAway region */
- break;
- }
- break;
-
- case keyDown:
- case autoKey: /* If command key, pass the char to MenuKey */
- if (curEvent.modifiers & cmdKey)
- userDone = DoMenuCommand(MenuKey((char)(curEvent.message & charCodeMask)));
- break;
- case updateEvt: /* If it’s for our window, update it */
- if ((WindowPtr)curEvent.message == gMainWindow)
- UpdateWindow(gMainWindow);
- break;
- case activateEvt: /* If for our window, set port as necessary */
- if ((WindowPtr)curEvent.message == gMainWindow) {
- if (curEvent.modifiers & 1) {
- /* odd means an activate event */
- SetPort(gMainWindow);
- DisableItem(GetMenu(kEditMenuID), 0);
- EnableItem(GetMenu(kFileMenuID), 0);
- EnableItem(GetMenu(kOptionsMenuID), 0);
- DrawMenuBar();
- }
- else {
- EnableItem(GetMenu(kEditMenuID), 0);
- DisableItem(GetMenu(kFileMenuID), 0);
- DisableItem(GetMenu(kOptionsMenuID), 0);
- DrawMenuBar();
- }
- }
- break;
- }
- }
- else {
- /* If there is no other event to handle (i.e. we got a null event)
- and we are in “continuous redraw” mode, we can calculate and display
- the next fractal. */
- if (gContinuousRedraw && !gTimeUpdate) {
- CalcSurface(gContourLevel);
- InvalRect(&gMainWindow->portRect);
- }
- }
- }
- }
-
-
- /*
- DoMenuCommand
-
- This function responds to the menu command returned by MenuSelect.
- If it was Quit, we return true, else false. Since the menu was
- highlighted by MenuSelect, we must finish by unhighlighting it
- to indicate we're done.
- */
- Boolean DoMenuCommand(long menuResult)
- {
- short menuID; /* menu ID of selected menu */
- short itemNumber; /* item number of selected menu item */
- Boolean quitSelected; /* was menu item Quit? */
-
- quitSelected = false; /* Assume Quit not selected */
- menuID = HiWord(menuResult); /* Get the menu selected */
- itemNumber = LoWord(menuResult); /* ... and the item of that menu */
-
- switch (menuID) {
- case kAppleMenuID:
- if (itemNumber == kAboutBoxItem) /* Tell about FracCont */
- DoAboutBox();
- else { /* Run a desk accessory */
- Str255 menuName;
- GrafPtr savedPort;
-
- GetPort(&savedPort); /* Preserve port */
- GetMenuItemText(GetMenu(kAppleMenuID), itemNumber, menuName);
- (void) OpenDeskAcc(menuName); /* Run the desk accessory */
- SetPort(savedPort); /* Restore port */
- }
- break;
-
- case kFileMenuID:
- switch (itemNumber) {
- case kNewFractalItem: /* New Surface */
- CalcSurface(gContourLevel);
- InvalRect(&gMainWindow -> portRect);
- break;
- case kQuitItem:
- quitSelected = true; /* Quit */
- break;
- }
- break;
-
- case kOptionsMenuID:
- switch (itemNumber) {
- case kSetupItem:
- DoSetUpDialog();
- break;
- case kContinuousItem:
- SetItemMark(GetMenu(kOptionsMenuID), kContinuousItem, gContinuousRedraw ?
- noMark : checkMark);
- gContinuousRedraw = !gContinuousRedraw;
- break;
- }
- break;
- }
-
- HiliteMenu(0); /* Turn off hilighting on the menu just used */
- return quitSelected;
- }
-
-
- /*
- SetTerrainButton
-
- This function is called by the DoSetUpDialog function to change
- the current terrain button.
- */
- void SetTerrainButton(DialogPtr theDialog, short newControlID, short oldControlID)
- {
- short itemType;
- ControlHandle theControl;
- Rect itemBox;
-
- GetDialogItem(theDialog, oldControlID, &itemType, (Handle*)&theControl, &itemBox);
- SetCtlValue(theControl, 0); /* Turn off old default button */
- GetDialogItem(theDialog, newControlID, &itemType, (Handle*)&theControl, &itemBox);
- SetCtlValue(theControl, 1); /* Turn on default button */
- }
-
-
- /*
- DoSetUpDialog
-
- This function displays the set-up dialog and handles user actions
- while the dialog is up.
- */
- void DoSetUpDialog(void)
- {
- DialogPtr theDialog; /* Pointer to dialog */
- long newType; /* Selected contour type */
- long newLevel; /* Selected level of detail */
- Boolean doAnother; /* True if we need to recalc */
- short itemHit = 0; /* Item number of selected item */
- Str255 itemText; /* Used for converting string to num */
- short itemType; /* Dummy variable needed for GetDialogItem */
- Handle itemHandle; /* Item handle returned by GetDialogItem */
- Rect itemBox; /* Item bound box returned by GetDialogItem */
-
- newType = gContourType; /* Initialize local vars to current values */
- newLevel = gContourLevel;
- doAnother = false;
-
- /* Display the dialog and set initial values if controls */
- theDialog = GetNewDialog(kSetUpDialogID, NULL, (WindowPtr) -1);
- SetTerrainButton(theDialog, gContourType, gContourType);
- NumToString(newLevel, itemText);
- GetDialogItem(theDialog, kSetUpLevelID, &itemType, &itemHandle, &itemBox);
- SetDialogItemText(itemHandle, itemText);
- SelIText(theDialog, kSetUpLevelID, 0, 32767); /* Hilite entire text field. */
-
- while (itemHit != kSetUpOKButtonID && itemHit != kSetUpCancelButtonID) {
- ModalDialog(NULL, &itemHit);
- GetDItem(theDialog, kSetUpLevelID, &itemType, &itemHandle, &itemBox);
- GetIText(itemHandle, itemText); /* Get Level field */
- StringToNum(itemText, &newLevel);
-
- /* Check for legal contour level */
- if (newLevel < 1 || newLevel > kMaxLevel) {
- SysBeep(0);
- newLevel = gContourLevel;
- NumToString(newLevel, itemText);
- GetDialogItem(theDialog, kSetUpLevelID, &itemType, &itemHandle, &itemBox);
- SetDialogItemText(itemHandle, itemText);
- SelIText(theDialog, kSetUpLevelID, 0, 100); /* Hilite text field. */
- }
-
- if (itemHit == kSetUpMtnButtonID ||
- itemHit == kSetUpHillsButtonID ||
- itemHit == kSetUpWaterButtonID) {
- SetTerrainButton(theDialog, itemHit, newType);
- newType = itemHit;
- }
-
- if (itemHit == kSetUpOKButtonID && ((newLevel > 0) & (newLevel <= kMaxLevel))) {
- if (gContourType != newType || gContourLevel != newLevel) {
- doAnother = true;
- InvalRect(&gMainWindow->portRect);
- gContourLevel = newLevel;
- gContourType = newType;
- }
- }
- }
-
- DisposeDialog(theDialog); /* Release storage and remove dialog from screen */
-
- if (doAnother) {
- gTotalTickCount = 0;
- gTotalFractals = 0;
- CalcSurface(newLevel);
- }
- }
-
-
- /*
- DoAboutBox
-
- This function displays the about box dialog.
- */
- void DoAboutBox(void)
- {
- short itemHit;
- DialogPtr theDialog;
-
- theDialog = GetNewDialog(kAboutBox1DialogID, NULL, (WindowPtr) -1);
- ModalDialog(NULL, &itemHit);
- DisposeDialog(theDialog);
-
- if (itemHit == kAboutBoxMoreButtonID) {
- theDialog = GetNewDialog(kAboutBox2DialogID, NULL, (WindowPtr) -1);
- ModalDialog(NULL, &itemHit);
- DisposeDialog(theDialog);
- }
- }
-
-
- /*
- SetUpMenus
-
- This function initilizes the menu bar and the applications menus.
- */
- void SetUpMenus(void)
- {
- SetMenuBar(GetNewMBar(kMenuBarID));
- AddResMenu(GetMenu(kAppleMenuID), 'DRVR');
- DrawMenuBar();
- }
-
-
- /*
- SetUpWindow
-
- This function initializes the main window and its associated
- global variables.
- */
- void SetUpWindow(void)
- {
- GDHandle mainDeviceHandle;
- short maxWindowHeight, maxWindowWidth;
-
- gMainWindow = GetNewCWindow(kMainWindowID, NULL, (WindowPtr)-1);
- mainDeviceHandle = GetMainDevice();
-
- maxWindowHeight = (*mainDeviceHandle)->gdRect.bottom - (*mainDeviceHandle)->gdRect.top -
- (2 * kScreenBoundaryBits) - GetMBarHeight() - kWindowTitleHeight;
- maxWindowWidth = (*mainDeviceHandle)->gdRect.right - (*mainDeviceHandle)->gdRect.left -
- (2 * kScreenBoundaryBits);
-
- if (maxWindowHeight >= kNewScreenY && maxWindowWidth >= kNewScreenX) {
- gMainWindowHeight = kNewScreenY;
- gMainWindowWidth = kNewScreenX;
- }
- else {
- gMainWindowHeight = maxWindowHeight;
- gMainWindowWidth = maxWindowWidth;
- }
-
- SizeWindow(gMainWindow, gMainWindowWidth, gMainWindowHeight, false);
- MoveWindow(gMainWindow,
- (*mainDeviceHandle)->gdRect.left + kScreenBoundaryBits,
- (*mainDeviceHandle)->gdRect.top + kScreenBoundaryBits + GetMBarHeight() + kWindowTitleHeight,
- false);
- ShowWindow(gMainWindow);
- }
-
-
- /*
- InitApp
-
- This function initializes the program the Mac toolbox.
- */
- void InitApp(void)
- {
- InitGraf(&qd.thePort);
- InitFonts();
- InitWindows();
- InitMenus();
- TEInit();
- InitDialogs(NULL);
- InitCursor();
-
- gContinuousRedraw = false;
- gTotalTickCount = 0; /* Initialize performance counters */
- gTotalFractals = 0;
-
- gContourType = kDefaultStyle; /* Default style */
- gContourLevel = kDefaultLevel; /* Default Level */
-
- SetUpWindow(); /* Create new window */
- SetUpMenus(); /* Set up our menus */
-
- /* Allocate the data array. */
- gPointArray = (void*)NewPtr((long) kMaxXPoint*kMaxYPoint*(sizeof(short)));
-
- /* Make sure we got some memory */
- if (gPointArray == NULL)
- ReportFatalError();
-
- CalcSurface(gContourLevel); /* Do at least one first */
- }
-
-
- /*
- UpdateWindow
-
- This is our response to receipt of an update event for
- gMainWindow. It redraws our main window, respecting the
- current clip region, etc.
- */
-
- void UpdateWindow(WindowPtr theWindow)
- {
- GrafPtr savedPort; /* Used to temporarily save cur port */
- CursHandle clockCursor; /* Used to display busy cursor */
- Rect windowRect; /* Rect of entire window */
- long startTicks; /* Used for performance timing */
-
- clockCursor = GetCursor(watchCursor);
- if (clockCursor)
- SetCursor(*clockCursor); /* Show busy cursor */
-
- GetPort(&savedPort); /* Save current port */
- SetPort(theWindow); /* Work in the specified window */
- BeginUpdate(theWindow);
-
- startTicks = TickCount(); /* Record current tick count */
-
- /* Erase portion of window that may change */
- SetRect(&windowRect, 0, 0, gMainWindowWidth, gMainWindowHeight - 20);
- EraseRect(&windowRect);
- SetRect(&windowRect, 200, gMainWindowHeight - 20, gMainWindowWidth, gMainWindowHeight);
- EraseRect(&windowRect);
- PlotData(); /* Redraw contents of window */
-
- /* If this is an update in response to a recomputation, we will count it
- as a part of the total time for the current fractal. If the update is
- in response to something else (e.g. the window coming to the front),
- we won’t time it. */
- if (gTimeUpdate) {
- gTimeUpdate = false;
- gTotalTickCount += TickCount() - startTicks;
- }
-
- DrawTimeInfo(); /* Draw performance statistics */
-
- SetPort(savedPort); /* Restore previous port */
- EndUpdate(theWindow);
- InitCursor(); /* Go back to arrow cursor */
- }
-
-
- /*
- DrawTimeInfo
- */
- void DrawTimeInfo(void)
- {
- Str255 tempString; /* Used for creating statistics string */
- Rect tempRect; /* Rect to erase */
-
- TextFont(geneva);
- TextSize(12);
- MoveTo(20, gMainWindowHeight - 5);
- DrawString("\pFractals per second: ");
-
- if (gTotalTickCount != 0) { /* Make sure we don’t divide by zero */
- SetRect(&tempRect, 150, gMainWindowHeight - 20, 200, gMainWindowHeight);
- EraseRect(&tempRect);
- sprintf((char*)tempString, "%3.3f",
- (double)gTotalFractals * 60 / (double)gTotalTickCount);
- c2pstr((char*)tempString);
- DrawString(tempString);
- }
- }
-
-
-
- /*
- ReportFatalError
-
- This function displays a fatal error alert and terminates the program.
- */
- void ReportFatalError(void)
- {
- (void) Alert(kFatalErrorAlertID, NULL);
- ExitToShell();
- }
-
-
-
-
-
-
-
-
-